package io.libp2p.example.chat;

import android.os.Handler;
import android.os.Looper;
import com.google.gson.Gson;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.nats.client.Connection;
import io.nats.client.ConnectionListener;
import io.nats.client.Nats;

public class NatsHelper {

    public static final String TAG = "NatsHelper";

    public static String Subscribe = "Webrtc.Subscribe";

    private Connection nats;

    private ConnectCallback callback;

    private ExecutorService executorService = Executors.newFixedThreadPool(1);

    private Handler handler = new Handler(Looper.getMainLooper());

    private Gson gson = new Gson();

    public interface SendMsgCallback{
        void onCallback(boolean scu);
    }

    private Map<String, SendMsgCallback> msgCallbackMap = new HashMap<>();

    public NatsHelper(ConnectCallback connectCallback){
        this.callback = connectCallback;

        if(BuildConfig.DEBUG) {
            Subscribe = Subscribe + ".debug";
        }

        MyLog.E(TAG,"NatsHelper Subscribe="+Subscribe);
    }

    public void startConnect(){
        executorService.execute(() -> connectNats());
    }

    public void connectNats(){
        try {
            nats = Nats.connect("nats://139.5.62.148:12222");
            //nats = Nats.connect("nats://5e1b5e6d-689d-4a61-bb97-a2446b0e0ada@115.190.89.224:27676");
            //nats = Nats.connect("nats://token@10.0.2.3:27676");

            MyLog.D(TAG, "Connection nats.getStatus()="+nats.getStatus());

            if(nats.getStatus() == Connection.Status.CONNECTED){
                runOnMainThread(() -> callback.onOpen());
            }

            nats.addConnectionListener(connectionListener);

            nats.createDispatcher().subscribe(Subscribe, (msg) -> {
                String response = new String(msg.getData());

                MyLog.D(TAG, "subscribe response="+response);

                SpdJson spdJson = gson.fromJson(response, SpdJson.class);

                if(spdJson != null) {
                    SendMsgCallback msgCallback = msgCallbackMap.remove(spdJson.msgId);

                    if(msgCallback == null) {
                        runOnMainThread(() -> callback.onMessage(spdJson));
                    }else{
                        msgCallback.onCallback(true);
                    }
                }
            });

        } catch (Exception e) {
            MyLog.D(TAG, "Nats.connect Exception:"+e);
            e.printStackTrace();
            runOnMainThread(() -> callback.onClose());
        }
    }

    public boolean sendMessage(String msg){
        boolean send = false;

        if(nats != null && nats.getStatus() == Connection.Status.CONNECTED){
            nats.publish(Subscribe, msg.getBytes());
            send = true;
            MyLog.E(TAG,"sendMessage msg="+msg);
        }else{
            MyLog.E(TAG,"sendMessage error, nats not connect!");
        }

        return send;
    }

    public void sendSpdJson(SpdJson spdJson){
        sendSpdJson(spdJson, null);
    }

    public void sendSpdJson(SpdJson spdJson, SendMsgCallback callback){
        handler.post(()->{
            String json = new Gson().toJson(spdJson);
            boolean send = sendMessage(json);

            if(callback != null) {
                if (send) {
                    msgCallbackMap.put(spdJson.msgId, callback);

                    handler.postDelayed(() -> {
                        SendMsgCallback msgCallback = msgCallbackMap.remove(spdJson.msgId);

                        if (msgCallback != null) {
                            msgCallback.onCallback(false);
                        }
                    }, 5 * 1000);
                } else {
                    callback.onCallback(false);
                }
            }
        });
    }

    public void reconnect(){
        close();

        startConnect();
    }

    public void close(){
        try {
            if(nats != null){
                nats.removeConnectionListener(connectionListener);
                nats.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void runOnMainThread(Runnable runnable){
        handler.post(runnable);
    }

    private ConnectionListener connectionListener = (conn, type) -> {
        MyLog.D(TAG, "Connection event="+type);

        if(type == ConnectionListener.Events.RECONNECTED){
            //runOnMainThread(() -> callback.onOpen());
        }
    };
}
